﻿using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Web;
using com.bodurov.SessionRecovery.Extensions;
using com.bodurov.SessionRecovery.Interfaces;

namespace com.bodurov.SessionRecovery
{
    public class SessionRecoveryConfiguration : ISessionRecoveryConfiguration
    {
        public const string CurrentLog = "CurrentLog.log";
        public const char SplitterChar = '_';
        public const string FileExtension = ".sess";

        private const string ConfigKey_SessionFilesFolder = "SessionRecovery.SessionFilesFolder";
        private const string ConfigKey_NumMinutesAfterWhichRestorIsNotAllowed = "SessionRecovery.NumMinutesAfterWhichRestorIsNotAllowed";
        private const string ConfigKey_TraceLevel = "SessionRecovery.TraceLevel";
        private const string ConfigKey_MaxNumberLogsToKeep = "SessionRecovery.MaxNumberLogsToKeep";

        private const string Default_SessionFilesFolder = "~/App_Data/SavedSessions";
        private const int Default_NumMinutesAfterWhichRestorIsNotAllowed = 30;
        private const TraceLevel Default_TraceLevel = TraceLevel.Verbose;
        private const int Default_MaxNumberLogsToKeep = 10;
        

        

        private string _sessionFilesFolder;
        private int _numMinutesAfterWhichRestorIsNotAllowed = -1;
        private TraceLevel _traceLevel = TraceLevel.Off;
        private int _maxNumberLogsToKeep = -1;
        private string _logFileName;

        public string SessionFilesFolder
        {
            get 
            {
                if(this._sessionFilesFolder == null)
                {
                    var folder = 
                        ConfigurationManager.AppSettings[ConfigKey_SessionFilesFolder] 
                        ?? Default_SessionFilesFolder;
                    if (folder.StartsWith("~") || folder.StartsWith("/"))
                    {
                        if (HttpContext.Current != null)
                        {
                            folder = HttpContext.Current.Server.MapPath(folder);
                        }
                    }
                    // this is needed if the path is defined relative to a test project
                    var dir = new DirectoryInfo(folder);
                    this._sessionFilesFolder = dir.FullName;
                }
                return this._sessionFilesFolder;
            }
        }

        public int NumMinutesAfterWhichRestorIsNotAllowed
        {
            get 
            {
                if (this._numMinutesAfterWhichRestorIsNotAllowed < 0)
                {
                    this._numMinutesAfterWhichRestorIsNotAllowed = 
                        ParseInt(
                                ConfigurationManager.AppSettings[ConfigKey_NumMinutesAfterWhichRestorIsNotAllowed], 
                                Default_NumMinutesAfterWhichRestorIsNotAllowed);
                }
                return this._numMinutesAfterWhichRestorIsNotAllowed;
            }
        }

        public TraceLevel TraceLevel
        { 
            get
            {
                if(this._traceLevel == TraceLevel.Off)
                {
                    this._traceLevel = 
                        ParseEnum(
                            ConfigurationManager.AppSettings[ConfigKey_TraceLevel],
                            Default_TraceLevel);
                }
                return this._traceLevel;
            }
        }

        public string LogFileName
        {
            get
            {
                if(this._logFileName == null)
                {
                    this._logFileName = Path.Combine(SessionFilesFolder, CurrentLog);
                }
                return this._logFileName;
            }
        }

        public int MaxNumberLogsToKeep
        {
            get
            {
                if (this._maxNumberLogsToKeep < 0)
                {
                    this._maxNumberLogsToKeep = 
                        ParseInt(
                                ConfigurationManager.AppSettings[ConfigKey_MaxNumberLogsToKeep],
                                Default_MaxNumberLogsToKeep);
                }
                return this._maxNumberLogsToKeep;
            }
        }

        public string GetFileName(string sessionID)
        {
            return Path.Combine(SessionFilesFolder, DateToString(DateTime.UtcNow) + SplitterChar + sessionID + FileExtension);
        }

        public bool FileIsSessionFile(string fileName)
        {
            if (String.IsNullOrEmpty(fileName))
            {
                return false;
            }
            if(!fileName.EndsWith(FileExtension))
            {
                return false;
            }
            var arr = fileName.Split(SplitterChar);
            return arr.Length >= 2 && arr[0].Length >= 17;
        }

        public string GetSessionIdByFileName(string fileName)
        {
            EnsureFileName(fileName);
            var arr = GetFileParts(fileName);
            return arr[1].Replace(FileExtension, "");
        }

        public DateTime GetDateTimeByFileName(string fileName)
        {
            EnsureFileName(fileName);
            var arr = GetFileParts(fileName);
            return StringToDate(arr[0]);
        }

        public string DateToString(DateTime date)
        {
            var sb = new StringBuilder();
            sb.Append(date.Year.ToString().PadLeft(4, '0'));
            sb.Append(date.Month.ToString().PadLeft(2, '0'));
            sb.Append(date.Day.ToString().PadLeft(2, '0'));
            sb.Append(date.Hour.ToString().PadLeft(2, '0'));
            sb.Append(date.Minute.ToString().PadLeft(2, '0'));
            sb.Append(date.Second.ToString().PadLeft(2, '0'));
            sb.Append(date.Millisecond.ToString().PadLeft(3, '0'));
            return sb.ToString();
        }
        public DateTime StringToDate(string str)
        {
            if(str == null || str.Length < 17)
            {
                throw new ArgumentException("Cannot turn the string '{0}' into date".Args(str));
            }
            return new DateTime(
                        ParseInt(str.Substring(0, 4)),
                        ParseInt(str.Substring(4, 2)),
                        ParseInt(str.Substring(6, 2)),
                        ParseInt(str.Substring(8, 2)),
                        ParseInt(str.Substring(10, 2)),
                        ParseInt(str.Substring(12, 2)),
                        ParseInt(str.Substring(14, 3))
                    );
        }

        private static void EnsureFileName(string fileName)
        {
            if (String.IsNullOrEmpty(fileName))
            {
                throw new ArgumentException("Filename must be specified when calling GetSessionIdByFileName");
            }
        }
        private static string[] GetFileParts(string fileName)
        {
            var parts = fileName.Split(SplitterChar);
            if (parts.Length < 2)
            {
                throw new ArgumentException("File '{0}' is not in the correct format".Args(fileName));
            }
            return parts;
        }
        private static int ParseInt(string setting)
        {
            return ParseInt(setting, 0);
        }
        private static int ParseInt(string setting, int defaultValue)
        {
            if (setting == null) return defaultValue;
            int value;
            return Int32.TryParse(setting, out value) ? value : defaultValue;
        }
        private static T ParseEnum<T>(string value, T defaultValue)
        {
            if (String.IsNullOrEmpty(value)) return defaultValue;
            try
            {
                return (T)Enum.Parse(typeof (T), value, true);
            }
            catch(FormatException)
            {
                return defaultValue;
            }
        }
    }
}
